package org.ouwa.lms.server.service;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.ouwa.lms.client.model.Account;
import org.ouwa.lms.client.model.Authentication;
import org.ouwa.lms.client.service.RemoteAccountService;
import org.ouwa.lms.client.support.exception.AppException;
import org.ouwa.lms.server.PersistenceManagerSingleton;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

@SuppressWarnings("serial")
public class RemoteAccountServiceImpl extends RemoteServiceServlet implements
		RemoteAccountService {
	private static final Logger LOG = Logger.getLogger(RemoteAccountServiceImpl.class.getName());
	private static Authentication authentication = Authentication.ANONYMOUS;
	
	@Override
	public Account createAccount(Account account) throws AppException {
		PersistenceManager pm = PersistenceManagerSingleton.getPersistenceManager();
    	Account newAccount = null;
    	
    	if(checkExistence(account, pm)) {
    		newAccount = new Account(account);
			try {
				pm.makePersistent(newAccount);
				LOG.log(Level.INFO, newAccount.toString() + " created.");
			}
			catch(Exception ex) {
				LOG.log(Level.SEVERE, ex.getMessage());
			}
			finally {
				pm.close();
			}
    	}
    	else {
    		throw new AppException("Email already exists.");
    	}
        
		return newAccount;
	}
	
	@SuppressWarnings("unchecked")
	private boolean checkExistence(Account account, PersistenceManager pm) {
		boolean result = true; // assuming no existing email
		Query query = pm.newQuery(Account.class);
		
		// Check for existing email address
		query.setFilter("email == emailParam");
		query.declareParameters("String emailParam");
		try {
			List<Account> accounts = (List<Account>) query.execute(account.getEmailAddress());
			if(!accounts.isEmpty()) {
				result = false;
			}
		}
		catch(Exception ex) {
			LOG.log(Level.SEVERE, ex.getMessage());
		}
		finally {
			query.closeAll(); // close the result collection and any associated resources
		}
		
		return result;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Account login(Authentication auth) throws AppException {
        if (auth == Authentication.ANONYMOUS) {
            throw new IllegalArgumentException("Cannot login anonymous authentication.");
        }
        
        PersistenceManager pm = PersistenceManagerSingleton.getPersistenceManager();
		Account account = null; // the retrieved account
		String email = auth.getUsername();
		Query query = pm.newQuery(Account.class);
		
        // Retrieve the account object by email
		query.setFilter("email == emailParam");
		query.declareParameters("String emailParam");
		try {
			List<Account> accounts = (List<Account>) query.execute(email);
			if(!accounts.isEmpty()) {
				account = accounts.get(0);
			}
		}
		catch(Exception ex) {
			LOG.log(Level.SEVERE, ex.getMessage());
		}
		finally {
			query.closeAll(); // close the result collection and any associated resources
		}
        
        if (account == null ) {
    		throw new AppException("Couldnot authenticate '" + email + "'.");
    	}
        authentication = new Authentication(email);
        
      	return account;
	}
	
	@Override
	public Authentication logout() {
		authentication = Authentication.ANONYMOUS;
		return authentication;
	}
	
	@Override
	public Authentication getCurrentAuthentication() {
		return authentication;
	}
	
	@Override
	public Account removeAccount(Authentication auth) throws AppException {
		PersistenceManager pm = PersistenceManagerSingleton.getPersistenceManager();
		Account account = login(auth);
		try {
			pm.deletePersistent(account);
			LOG.log(Level.INFO, account.toString() + " removed.");
		}
		catch(Exception ex) {
			LOG.log(Level.SEVERE, ex.getMessage());
		}
		finally {
			pm.close();
		}
		
		return account;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Account updateAccount(Account account) throws AppException {
		PersistenceManager pm = PersistenceManagerSingleton.getPersistenceManager();
		Query query = pm.newQuery(Account.class);
		Account updatedAccount = null;
		
		if(!checkExistence(account, pm)) { // username exists
			// Retrieve the account object by email
			query.setFilter("email == emailParam");
			query.declareParameters("String emailParam");
			try {
				List<Account> accounts = (List<Account>) query.execute(account.getEmailAddress());
				if(!accounts.isEmpty()) {
					updatedAccount = accounts.get(0);
					String newName = account.getName();
					
					if(!account.getName().equals(newName)) {
						updatedAccount.setName(newName);
					}
					LOG.log(Level.INFO, updatedAccount.toString() + " updated.");
				}
			}
			catch(Exception ex) {
				LOG.log(Level.SEVERE, ex.getMessage());
			}
			finally {
				query.closeAll(); // close the result collection and any associated resources
			}
		}
		else {
			throw new AppException("Account does not exist.");
		}
		
		return updatedAccount;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Account getAccount(String email)
			throws AppException {
		PersistenceManager pm = PersistenceManagerSingleton.getPersistenceManager();
		Query query = pm.newQuery(Account.class);
		Account account = null;
		
		query.setFilter("email == emailParam");
		query.declareParameters("String emailParam");
		try {
			List<Account> accounts = (List<Account>) query.execute(email);
			if(!accounts.isEmpty()) {
				account = accounts.get(0);
			}
		}
		catch(Exception ex) {
			LOG.log(Level.SEVERE, ex.getMessage());
		}
		finally {
			query.closeAll(); // close the result collection and any associated resources
		}
		
		return account;
	}
}
